package cn.lili.modules.member.serviceimpl;

import cn.lili.cache.Cache;
import cn.lili.cache.CachePrefix;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.event.TransactionCommitSendMQEvent;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.security.enums.SceneEnums;
import cn.lili.common.utils.StringUtils;
import cn.lili.common.utils.UuidUtils;
import cn.lili.exchange.AmqpExchangeProperties;
import cn.lili.modules.member.entity.dos.User;
import cn.lili.modules.member.mapper.UserMapper;
import cn.lili.modules.member.service.UserSecurityService;
import cn.lili.modules.member.service.UserService;
import cn.lili.routing.UserRoutingKey;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;

/**
 * 用户安全 接口业务层实现
 *
 * @author Chopper
 * @since 2021-03-29 14:10:16
 */
@Service
@RequiredArgsConstructor
public class UserSecurityServiceImpl extends ServiceImpl<UserMapper, User> implements UserSecurityService {


    /**
     * 缓存
     */
    private final Cache cache;

    private final UserService userService;

    private final ApplicationEventPublisher applicationEventPublisher;

    private final AmqpExchangeProperties amqpExchangeProperties;


    @Override
    public User modifyPass(String oldPassword, String newPassword) {
        AuthUser authUser = UserContext.getCurrentUser();
        if (authUser == null) {
            throw new ServiceException(ResultCode.USER_NOT_LOGIN);
        }
        User member = this.getById(authUser.getId());
        //判断旧密码输入是否正确
        if (!new BCryptPasswordEncoder().matches(oldPassword, member.getPassword())) {
            throw new ServiceException(ResultCode.USER_OLD_PASSWORD_ERROR);
        }
        //修改会员密码
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
        lambdaUpdateWrapper.eq(User::getId, member.getId());
        lambdaUpdateWrapper.set(User::getPassword, new BCryptPasswordEncoder().encode(newPassword));
        this.update(lambdaUpdateWrapper);
        return member;
    }

    @Override
    public boolean canInitPass() {
        AuthUser tokenUser = UserContext.getCurrentUser();
        if (tokenUser == null) {
            throw new ServiceException(ResultCode.USER_NOT_LOGIN);
        }
        User member = this.getById(tokenUser.getId());
        return member.getPassword().equals(UserService.DEFAULT_PASSWORD);

    }

    @Override
    public void initPass(String password) {
        AuthUser tokenUser = UserContext.getCurrentUser();
        if (tokenUser == null) {
            throw new ServiceException(ResultCode.USER_NOT_LOGIN);
        }
        User member = this.getById(tokenUser.getId());
        if (member.getPassword().equals(UserService.DEFAULT_PASSWORD)) {
            //修改会员密码
            LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
            lambdaUpdateWrapper.eq(User::getId, member.getId());
            lambdaUpdateWrapper.set(User::getPassword, new BCryptPasswordEncoder().encode(password));
            this.update(lambdaUpdateWrapper);
        }
        throw new ServiceException(ResultCode.UNINITIALIZED_PASSWORD);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancellation(String password) {

        AuthUser tokenUser = UserContext.getCurrentUser();
        if (tokenUser == null) {
            throw new ServiceException(ResultCode.USER_NOT_LOGIN);
        }
        User user = this.getById(tokenUser.getId());
        if (user.getPassword().equals(new BCryptPasswordEncoder().encode(password))) {
            //混淆用户信息
            this.confusionMember(user);
            //todo 删除联合登录


            applicationEventPublisher.publishEvent(
                    TransactionCommitSendMQEvent.builder()
                            .source("USER_DELETE")
                            .exchange(amqpExchangeProperties.getUser())
                            .routingKey(UserRoutingKey.DELETE)
                            .message(user.getId())
                            .build()
            );
        }
    }

    /**
     * 混淆之前的会员信息
     *
     * @param member
     */
    private void confusionMember(User member) {
        member.setUsername(UuidUtils.getUUID());
        member.setMobile(UuidUtils.getUUID() + member.getMobile());
        member.setNickName("用户已注销");
        member.setEnable(false);
        this.updateById(member);
    }

    @Override
    public boolean changeMobile(String mobile, SceneEnums sceneEnums) {
        AuthUser tokenUser = Objects.requireNonNull(UserContext.getCurrentUser());
        User user = userService.findByUsername(tokenUser.getUsername(), sceneEnums);

        //判断是否用户登录并且会员ID为当前登录会员ID
        if (!Objects.equals(tokenUser.getId(), user.getId())) {
            throw new ServiceException(ResultCode.USER_NOT_LOGIN);
        }
        //修改会员手机号
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
        lambdaUpdateWrapper.eq(User::getId, user.getId());
        lambdaUpdateWrapper.set(User::getMobile, mobile);
        return this.update(lambdaUpdateWrapper);
    }

    @Override
    public boolean resetPasswordSign(String uuid, String mobile, SceneEnums scene) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mobile", mobile);
        queryWrapper.eq("scene", scene.value());
        User member = this.baseMapper.selectOne(queryWrapper);
        if (member == null) {
            throw new ServiceException(ResultCode.USER_NOT_PHONE);
        }
        cache.put(CachePrefix.FIND_MOBILE + uuid, mobile, 300L);
        return true;
    }

    @Override
    public boolean resetPasswordByMobile(String uuid, String password, SceneEnums scene) {
        String phone = cache.get(CachePrefix.FIND_MOBILE + uuid).toString();
        //根据手机号获取会员判定是否存在此会员
        if (phone != null) {
            //修改密码
            LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
            lambdaUpdateWrapper.eq(User::getMobile, phone);
            lambdaUpdateWrapper.eq(User::getScene, scene.value());
            lambdaUpdateWrapper.set(User::getPassword, new BCryptPasswordEncoder().encode(password));
            cache.remove(CachePrefix.FIND_MOBILE + uuid);
            return this.update(lambdaUpdateWrapper);
        } else {
            throw new ServiceException(ResultCode.USER_PHONE_NOT_EXIST);
        }

    }

    /**
     * 根据手机号获取会员
     *
     * @param mobilePhone 手机号
     * @param userName    用户名
     * @param scene       场景
     * @return 会员
     */
    private Long findUser(String mobilePhone, String userName, String scene) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getScene, scene).and(i -> {
            i.eq(User::getMobile, mobilePhone).or().eq(User::getUsername, userName);
        });
        return this.baseMapper.selectCount(queryWrapper);
    }


    @Override
    public void resetPassword(List<String> ids, String password) {

        //加密前端传递的密码
        if (StringUtils.isNotBlank(password)) {
            password = new BCryptPasswordEncoder().encode(password);
        }
        //如果没有传入重置密码，则默认重置为123456
        String defaultPassword = new BCryptPasswordEncoder().encode(StringUtils.md5("123456"));
        LambdaUpdateWrapper<User> lambdaUpdateWrapper = Wrappers.lambdaUpdate();

        //如果不是管理员，则增加场景和扩展ID 确保数据安全
        if (!UserContext.getCurrentUser().getScene().equals(SceneEnums.MANAGER)) {
            lambdaUpdateWrapper.eq(User::getScene, UserContext.getCurrentUser().getScene());
            lambdaUpdateWrapper.eq(User::getExtendId, UserContext.getCurrentUser().getExtendId());
        }

        lambdaUpdateWrapper.in(User::getId, ids);

        //写入新的密码
        if (StringUtils.isBlank(password)) {
            lambdaUpdateWrapper.set(User::getPassword, defaultPassword);
        } else {
            lambdaUpdateWrapper.set(User::getPassword, password);
        }


        this.update(lambdaUpdateWrapper);
    }

}